import { FontLoader } from 'three/addons/loaders/FontLoader.js';
import { BaseModel, loadTexture } from '@/plugin';

export default class Point extends BaseModel {
  constructor(options) {
    super();

    this.options = {
      scale: 1,
      points: [],
      ...options,
    };

    this.#init();
  }

  #init() {
    const { scale, points } = this.options;

    this.object.name = 'point';

    points.forEach((v) => {
      const point = this.#createPoint(v);
      point.scale.set(scale, scale, scale);
      this.object.add(point);
    });

    const delegatingHandler = (fn) => {
      return (name) => {
        const obj = this.object.getObjectByName(`point-${name}`);
        if (obj) {
          fn(obj);
        }
      };
    };

    this.on(
      'point:active',
      delegatingHandler((obj) => {
        obj.visible = true;
        obj.tween.start();
      })
    );

    this.on(
      'point:deactive',
      delegatingHandler((obj) => {
        obj.visible = false;
        obj.tween.stop();
      })
    );
  }

  #createPoint(point) {
    const group = new THREE.Group();
    group.position.set(...point.center, 0);

    const circle = this.#createBottomCircle();
    group.add(circle);

    const activeCircle = this.#createActiveCircle();
    activeCircle.name = `point-${point.name}`;
    group.add(activeCircle);

    const pillar = this.#createPillar();
    group.add(pillar);

    const label = this.#createLabel(point);
    label.position.z = 3.2;
    group.add(label);

    return group;
  }

  #createBottomCircle(color) {
    const group = new THREE.Group();

    const url1 = require('./assets/circle1.png');
    const texture1 = loadTexture(url1);
    texture1.center.set(0.5, 0.5);

    const url2 = require('./assets/circle2.png');
    const texture2 = loadTexture(url2);

    const tween = new TWEEN.Tween(texture1, this.tweenGroup).to({ rotation: Math.PI * 2 }, 2 * 1000).repeat(Infinity);
    this.tweenList.push(tween);

    const geometry = new THREE.PlaneGeometry();
    const material = new THREE.MeshBasicMaterial({
      color: 0xffffff,
      alphaMap: texture1,
      transparent: true,
      side: THREE.DoubleSide,
      depthWrite: false,
    });
    const circle1 = new THREE.Mesh(geometry, material);

    const material2 = material.clone();
    material2.alphaMap = texture2;
    material2.color = new THREE.Color(0xaef7f0);
    const circle2 = new THREE.Mesh(geometry, material2);

    group.add(circle1);
    group.add(circle2);

    return group;
  }

  #createActiveCircle(color, point) {
    const url = require('./assets/circle3.png');
    const texture = loadTexture(url);
    texture.center.set(0.5, 0.5);

    const geometry = new THREE.PlaneGeometry();
    const material = new THREE.MeshBasicMaterial({
      alphaMap: texture,
      transparent: true,
      side: THREE.DoubleSide,
      depthWrite: false,
    });
    const mesh = new THREE.Mesh(geometry, material);
    mesh.visible = false;

    mesh.tween = new TWEEN.Tween(mesh, this.tweenGroup)
      .to({ scale: { x: 4, y: 4 }, material: { opacity: 0 } }, 1000)
      .repeat(Infinity);

    return mesh;
  }

  #createPillar(color) {
    const group = new THREE.Group();
    group.renderOrder = 1;

    const url = require('./assets/pillar.png');
    const texture = loadTexture(url);

    const geometry = new THREE.PlaneGeometry(0.6, 3);
    const material = new THREE.MeshBasicMaterial({
      color: 0xadffff,
      map: texture,
      transparent: true,
      side: THREE.DoubleSide,
      depthWrite: false,
    });
    const mesh = new THREE.Mesh(geometry, material);
    mesh.rotation.x = Math.PI / 2;
    mesh.rotation.y = Math.PI / 4;
    mesh.position.z = 1.5;
    group.add(mesh);

    const mesh2 = mesh.clone();
    mesh2.rotation.y = -Math.PI / 4;
    group.add(mesh2);

    return group;
  }

  #createLabel(point) {
    const { name } = point;

    const group = new THREE.Group();
    group.rotation.x = Math.PI / 2;

    // 文本
    const url = require('./assets/Alibaba PuHuiTi.json');
    const font = new FontLoader().parse(url);

    const shapes = font.generateShapes(name, 0.25);
    const geometry = new THREE.ShapeGeometry(shapes);

    geometry.computeBoundingBox();

    const textWidth = geometry.boundingBox.max.x - geometry.boundingBox.min.x;
    const textHeight = geometry.boundingBox.max.y - geometry.boundingBox.min.y;

    geometry.translate(-textWidth / 2, textHeight / 2, 0.01);

    const material = new THREE.MeshBasicMaterial({
      color: 0xffffff,
      transparent: true,
      side: THREE.DoubleSide,
    });

    const textMesh = new THREE.Mesh(geometry, material);
    group.add(textMesh);

    // 背景
    const planeWidth = textWidth + 1;
    const planeHeight = textHeight * 1.8;
    const planeShape = this.#roundedRect(0, 0, planeWidth, planeHeight, planeHeight / 2);
    const planeGeometry = new THREE.ShapeGeometry(planeShape);
    planeGeometry.translate(-planeWidth * 0.5, 0, 0);

    const planeMaterial = new THREE.MeshBasicMaterial({
      color: 0x08523b,
      transparent: true,
      opacity: 0.5,
      side: THREE.DoubleSide,
    });
    const planeMesh = new THREE.Mesh(planeGeometry, planeMaterial);
    group.add(planeMesh);

    return group;
  }

  #roundedRect(x, y, width, height, radius) {
    const shape = new THREE.Shape();

    shape.moveTo(x, y + radius);
    shape.lineTo(x, y + height - radius);
    shape.quadraticCurveTo(x, y + height, x + radius, y + height);
    shape.lineTo(x + width - radius, y + height);
    shape.quadraticCurveTo(x + width, y + height, x + width, y + height - radius);
    shape.lineTo(x + width, y + radius);
    shape.quadraticCurveTo(x + width, y, x + width - radius, y);
    shape.lineTo(x + radius, y);
    shape.quadraticCurveTo(x, y, x, y + radius);

    return shape;
  }
}
